﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Serialization;
using System.Linq.Expressions;

namespace Objects
{
    public interface IRepository
    {
        void Remove(string Namespace, Guid key);
        void Clear();
        object this[Guid index] { get; set; }
        object this[string Namespace, Guid key] { get; set; }
        IQueryable<T> Query<T>(Expression exp);
    }

    public interface IIndexAccess<Tindx, Tvalue>
    {
        Tvalue this[Tindx index] { get; set; }
    }

    /// <summary>
    /// Потокозащищенный реестр объектов (+для каждого потока свой внутренний и внешний реестр)
    /// </summary>
    public class Registry : IRepository
    {
        public struct Key
        {
            public string Namespace;
            public Guid Id;
            public static bool operator ==(Key left, Key right)
            {
                return left.Namespace == right.Namespace && left.Id == right.Id;
            }
            public static bool operator !=(Key left, Key right)
            {
                return left.Namespace != right.Namespace || left.Id != right.Id;
            }
            public override int GetHashCode()
            {
                return (Namespace != null) ? Namespace.GetHashCode() : 0
                    ^ (Id.GetHashCode() ^ int.MaxValue);
            }
        }
        public class Namespace : Dictionary<Guid, object>
        {
        }
        protected IRepository extRepos;


        protected static readonly SynchronizedDictionary<Thread, Registry> regs = new SynchronizedDictionary<Thread, Registry>();
        /// <summary>
        /// Внешний реестр, к которому будут отосланы запросы в случае не попадания ключа
        /// </summary>
        public static IRepository extRegistry
        {
            get
            {
                return Instance.extRepos;
            }
            set
            {
                Instance.extRepos = value;
            }
        }
        public static Registry Instance
        {
            get
            {
                return GetOrCreateRegistry();
            }
        }

        public virtual void Remove(string Namespace, Guid key)
        {
            Namespace ns;
            if (objs.TryGetValue(Namespace, out ns))
            {
                ns.Remove(key);
                if (ns.Count == 0)
                    objs.Remove(Namespace);
            }
        }

        public virtual void Remove(Guid key)
        {
            Remove("", key);
        }

        protected static Registry GetOrCreateRegistry()
        {
            Registry res;
            Thread t = Thread.CurrentThread;
            if (!regs.TryGetValue(t, out res))
            {
                res = new Registry();
                regs.Add(t, res);
            }
            return res;
        }
        protected Registry() { }

        protected Dictionary<string, Namespace> objs = new Dictionary<string, Namespace>();
        public IDictionary<string, Namespace> All
        {
            get { return objs; }
        }
        protected virtual void AddOrSetObject(string Namespace, Guid key, object obj)
        {
            Namespace ns;
            if (!objs.TryGetValue(Namespace, out ns))
                objs.Add(Namespace, ns = new Namespace());
            ns.AddOrSetValue(key, obj);
        }

        public virtual void Clear()
        {
            objs.Clear();
        }
        #region IIndexAccess<Guid,object> Members

        public virtual object this[Guid index]
        {
            get
            {
                return this["", index];
            }
            set
            {
                this["", index] = value;
            }
        }

        public virtual object this[string Namespace, Guid key]
        {
            get
            {
                Namespace ns;
                object res = null;
                if ((!objs.TryGetValue(Namespace, out ns) || !ns.TryGetValue(key, out res)) && extRepos != null)
                    res = extRepos[Namespace, key];
                return res;
            }
            set
            {
                AddOrSetObject(Namespace, key, value);
            }
        }


        #endregion

        #region IRepository Members

        public IQueryable<T> Query<T>(Expression exp)
        {
            switch (exp.NodeType)
            {
                case ExpressionType.Add:
                    break;
                case ExpressionType.AddChecked:
                    break;
                case ExpressionType.And:
                    break;
                case ExpressionType.AndAlso:
                    break;
                case ExpressionType.ArrayIndex:
                    break;
                case ExpressionType.ArrayLength:
                    break;
                case ExpressionType.Call:
                    break;
                case ExpressionType.Coalesce:
                    break;
                case ExpressionType.Conditional:
                    break;
                case ExpressionType.Constant:
                    break;
                case ExpressionType.Convert:
                    break;
                case ExpressionType.ConvertChecked:
                    break;
                case ExpressionType.Divide:
                    break;
                case ExpressionType.Equal:
                    break;
                case ExpressionType.ExclusiveOr:
                    break;
                case ExpressionType.GreaterThan:
                    break;
                case ExpressionType.GreaterThanOrEqual:
                    break;
                case ExpressionType.Invoke:
                    break;
                case ExpressionType.Lambda:
                    break;
                case ExpressionType.LeftShift:
                    break;
                case ExpressionType.LessThan:
                    break;
                case ExpressionType.LessThanOrEqual:
                    break;
                case ExpressionType.ListInit:
                    break;
                case ExpressionType.MemberAccess:
                    break;
                case ExpressionType.MemberInit:
                    break;
                case ExpressionType.Modulo:
                    break;
                case ExpressionType.Multiply:
                    break;
                case ExpressionType.MultiplyChecked:
                    break;
                case ExpressionType.Negate:
                    break;
                case ExpressionType.NegateChecked:
                    break;
                case ExpressionType.New:
                    break;
                case ExpressionType.NewArrayBounds:
                    break;
                case ExpressionType.NewArrayInit:
                    break;
                case ExpressionType.Not:
                    break;
                case ExpressionType.NotEqual:
                    break;
                case ExpressionType.Or:
                    break;
                case ExpressionType.OrElse:
                    break;
                case ExpressionType.Parameter:
                    break;
                case ExpressionType.Power:
                    break;
                case ExpressionType.Quote:
                    break;
                case ExpressionType.RightShift:
                    break;
                case ExpressionType.Subtract:
                    break;
                case ExpressionType.SubtractChecked:
                    break;
                case ExpressionType.TypeAs:
                    break;
                case ExpressionType.TypeIs:
                    break;
                case ExpressionType.UnaryPlus:
                    break;
                default:
                    break;
            }
            return null;
        }

        #endregion
    }
}
